#include <algorithm>
#include <sstream>
#include <vector>
#include <numeric>
#include <functional>
#include "polynomial.h"

Polynomial::Polynomial() {
    power = 0;
}

Polynomial::Polynomial(int number) {
    power = 0;
    terms.push_back(number);
}

Polynomial::Polynomial(const Term& t) {
    power = t.power;
    terms.push_back(t);
}

Polynomial::Polynomial(const Polynomial& p2) = default;

Polynomial::Polynomial(const std::string& inp) {
    std::stringstream stream(inp);
    stream >> *this;
    for (Term t : terms) {
        power = std::max(power, t.power);
    }
}

Polynomial& Polynomial::operator=(const Polynomial& p2) {
    if (this != &p2) {
        terms = p2.terms;
        power = p2.power;
        order_ = p2.order_;
    }
    return *this;
}

void sort(Polynomial& p) {
    sort(p.terms.begin(), p.terms.end());
    if (!p.order_) {
        reverse(p.terms.begin(), p.terms.end());
    }
}

Polynomial& Polynomial::operator+=(const Polynomial& p2) {
    for (const Term& t : p2.terms) {
        *this += t;
    }
    sort(*this);
    return *this;
}

Polynomial& Polynomial::operator+=(const Term& t2) {
    auto cond = [t2](Term t) {return t.power == t2.power; };
    auto t1 = find_if(terms.begin(), terms.end(), cond);
    if (t1 != terms.end()) {
        t1->coef += t2.coef;
        return *this;
    }
    terms.push_back(t2);
    return *this;
}

Polynomial& Polynomial::operator*=(const Polynomial& p2) {
    Polynomial res;
    for (int i = 0; i < terms.size(); i++) {
        for (int j = 0; j < p2.terms.size(); j++) {
            Term t1(terms[i]);
            Term t2(p2.terms[j]);
            res += t1 * t2;
        }
    }
    sort(res);
    *this = res;
    return *this;
}

Polynomial operator*(const Polynomial& p1, const Polynomial& p2) {
    Polynomial res;
    for (int i = 0; i < p1.terms.size(); i++) {
        for (int j = 0; j < p2.terms.size(); j++) {
            Term t1(p1.terms[i]);
            Term t2(p2.terms[j]);
            res += t1 * t2;
        }
    }
    sort(res);
    return res;
}

Polynomial operator+(const Polynomial& p1, const Polynomial& p2) {
    Polynomial result(p1);
    result += p2;
    sort(result);
    return result;
}

std::istream& operator>>(std::istream& is, Polynomial& p) {
    p.terms.clear();
    std::string input;
    getline(is, input);
    input.erase(remove_if(input.begin(), input.end(), ::isspace), input.end());
    Term term;
    size_t end1 = input.find('-');
    end1 = (end1 == std::string::npos) ? 9999 : end1;
    size_t end2 = input.find('+');
    end2 = (end2 == std::string::npos) ? 9999 : end2;
    size_t end = std::min(end1, end2);
    bool neg = end1 < end2;
    while (end != 9999) {
        std::stringstream termIss(((neg) ? "-" : "") + input.substr(0, end));
        termIss >> term;
        p += term;
        input.erase(input.begin(), input.begin() + end + 1);
        end1 = input.find('-');
        end1 = (end1 == -1) ? 9999 : end1;
        end2 = input.find('+');
        end2 = (end2 == -1) ? 9999 : end2;
        end = std::min(end1, end2);
        neg = end1 < end2;
    }
    std::stringstream termIss(((neg) ? "-" : "") + input.substr(0, end));
    termIss >> term;
    p += term;
    return is;
}

std::ostream& operator<<(std::ostream& os, Polynomial p) {
    sort(p);
    if (p.terms.empty()) {
        os << "0";
    }
    else {
        for (auto it = p.terms.begin(); it != p.terms.end(); ++it) {
            os << *it;
            if (it != p.terms.end() - 1) {
                os << " + ";
            }
        }
    }
    return os;
}